package com.xinqi.common.mybatis.type;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.xinqi.common.base.utils.json.JSON;
import com.xinqi.common.mybatis.config.MyResultSet;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

@SuppressWarnings("unchecked")
public class JsonObjectListTypeHandler extends BaseTypeHandler<List> {

    private static final ConcurrentHashMap<String, Class> cache = new ConcurrentHashMap();

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List parameter, JdbcType jdbcType)
        throws SQLException {
        ps.setString(i, JSON.stringify(parameter));
    }

    @Override
    public List getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Class actualClass = getActualClass(rs, columnName);
        String result = rs.getString(columnName);
        return toJsonNode(result, actualClass);
    }

    @Override
    public List getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        Class actualClass = getActualClass(rs, getColumnName(rs.getMetaData(), columnIndex));
        String result = rs.getString(columnIndex);
        return toJsonNode(result, actualClass);
    }

    @Override
    public List getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        Class actualClass = getActualClass(cs.getResultSet(), getColumnName(cs.getMetaData(), columnIndex));
        String result = cs.getString(columnIndex);
        return toJsonNode(result, actualClass);
    }

    /**
     * 代理实现
     */
    private Class getActualClass(ResultSet rs, String columnName) {
        if (rs instanceof MyResultSet) {
            MyResultSet myResultSet = (MyResultSet) rs;
            Class<?> type = myResultSet.getResultType();
            ResultMapping resultMapping = myResultSet.getResultMapping();
            String className = type.getName();
            String fileName;
            if (resultMapping == null) {
                fileName = StringUtils.underlineToCamel(columnName);
            } else {
                fileName = resultMapping.getProperty();
            }
            try {
                if (fileName == null) {
                    throw new RuntimeException("not fund mapping property" + columnName + "class " + className);
                }
                return getaClassInCache(type, className, fileName);
            } catch (Exception var12) {
                var12.printStackTrace();
            }
        }
        return null;
    }

    private Class getaClassInCache(Class<?> type, String className, String fileName) throws NoSuchFieldException {
        String cacheKey = className + "." + fileName;
        Class aClass = cache.get(cacheKey);
        if (aClass == null) {
            Field field = type.getDeclaredField(fileName);
            Type genericType = field.getGenericType();
            if (genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                aClass = (Class) pt.getActualTypeArguments()[0];
                cache.put(cacheKey, aClass);
            }
        }
        return aClass;
    }

    private List toJsonNode(String s, Class aClass) {
        if (s == null) {
            return null;
        }
        try {
            return JSON.parseList(s, aClass);
        } catch (Exception e) {
            throw new IllegalArgumentException("Cannot convert [ " + s + " ] to JsonNode.", e);
        }
    }

    private String getColumnName(ResultSetMetaData metaData, int columnIndex) throws SQLException {
        return metaData.getColumnName(columnIndex);
    }

}
